use crate::lexer::{Position, SpannedToken, Token,Keyword,Symbol,Literal};
pub struct Lexer {
    input: Vec<char>,
    position: usize,
    line: usize,
    column: usize,
}

impl Lexer {
    pub fn new(input: &str) -> Self {
        Lexer {
            input: input.chars().collect(),
            position: 0,
            line: 1,
            column: 1,
        }
    }

    fn peek(&self) -> Option<char> {
        self.input.get(self.position).copied()
    }

    fn next(&mut self) -> Option<char> {
        let ch = self.input.get(self.position).copied();
        if let Some(c) = ch {
            self.position += 1;
            if c == '\n' {
                self.line += 1;
                self.column = 1;
            } else {
                self.column += 1;
            }
        }
        ch
    }

    fn current_position(&self) -> Position {
        Position {
            line: self.line,
            column: self.column,
        }
    }
    pub fn tokenize(&mut self) -> Vec<SpannedToken> {
        let mut tokens = Vec::new();

        while let Some(ch) = self.peek() {
            let pos = self.current_position();
            match ch {
                c if c.is_whitespace() => {
                    if c == '\n' {
                        tokens.push(SpannedToken {
                            token: Token::Newline,
                            position: pos,
                        });
                    } else {
                        tokens.push(SpannedToken {
                            token: Token::Whitespace,
                            position: pos,
                        });
                    }
                    self.next();
                }
                c if c.is_alphabetic() || c == '_' => {
                 let ident = self.read_identifier();
                    if let Some(keyword) = Self::to_keyword(&ident) {
                        tokens.push(SpannedToken {
                            token: Token::Keyword(keyword),
                            position: pos,
                        });
                    } else {
                        tokens.push(SpannedToken {
                            token: Token::Identifier(ident),
                            position: pos,
                        });
                    }
                }
                c if c.is_digit(10) => {
                    let num = self.read_number();
                    if num.contains('.') {
                        if let Ok(f) = num.parse::<f64>() {
                            tokens.push(SpannedToken {
                                token: Token::Literal(Literal::Float(f)),
                                position: pos,
                            });
                        }
                    } else {
                        if let Ok(i) = num.parse::<i64>() {
                            tokens.push(SpannedToken {
                                token: Token::Literal(Literal::Integer(i)),
                                position: pos,
                            });
                        }
                    }
                }
                '"' => {
                    let string = self.read_string();
                    tokens.push(SpannedToken {
                        token: Token::Literal(Literal::String(string)),
                        position: pos,
                    });
                }
                '/' => {
                       if self.peek_ahead(1) == Some('/') {
                        let comment = self.read_comment();
                        tokens.push(SpannedToken {
                            token: Token::Comment(comment),
                            position: pos,
                        });
                    } else {
                        tokens.push(SpannedToken {
                            token: Token::Symbol(Symbol::Slash),
                            position: pos,
                        });
                        self.next();
                    }
                }
                c if Self::is_symbol(c) => {
                    if let Some(sym) = Self::to_symbol(c) {
                        tokens.push(SpannedToken {
                            token: Token::Symbol(sym),
                            position: pos,
                        });
                    }
                    self.next();
                }
                _ => {
                    self.next(); // skip unknown
                }
            }
        }
        tokens.push(SpannedToken {
            token: Token::EOF,
            position: self.current_position(),
        });
        tokens
    }

    fn read_identifier(&mut self) -> String {
        let mut ident = String::new();
        while let Some(ch) = self.peek() {
            if ch.is_alphanumeric() || ch == '_' {
                ident.push(ch);
                self.next();
            } else {
                break;
            }
        }
        ident
    }

    fn read_number(&mut self) -> String {
        let mut num = String::new();
        while let Some(ch) = self.peek() {
            if ch.is_digit(10) || ch == '.' {
                num.push(ch);
                self.next();
            } else {
                break;
            }
        }
        num
    }

    fn read_string(&mut self) -> String {
        let mut string = String::new();
        self.next(); // skip opening "
        while let Some(ch) = self.peek() {
            if ch == '"' {
                self.next(); // skip closing "
                break;
            } else {
                string.push(ch);
                self.next();
            }
        }
        string
    }

    fn read_comment(&mut self) -> String {
        let mut comment = String::new();
        self.next(); // skip first /
        self.next(); // skip second /
        while let Some(ch) = self.peek() {
            if ch == '\n' {
                break;
            } else {
                comment.push(ch);
                self.next();
            }
        }
        comment
    }

    fn peek_ahead(&self, n: usize) -> Option<char> {
        self.input.get(self.position + n).copied()
    }
    fn is_keyword(s: &str) -> bool {
        matches!(
            s,
            "package"
                | "import"
                | "fn"
                | "if"
                | "else"
                | "elif"
                | "main"
                | "for"
                | "return"
                | "true"
                | "false"
                | "auto"
                | "null"
                | "const"
                | "static"
                | "final"
                | "run"
                | "break"
                | "continue"
                | "match"
                | "in"
                | "int"
                | "int8"
                | "int16"
                | "int32"
                | "int64"
                | "uint"
                | "uint8"
                | "uint16"
                | "uint32"
                | "uint64"
                | "float32"
                | "float64"
                | "string"
                | "bool"
                | "void"
                | "char"
                | "enum"
                | "list"
                | "tuple"
                | "channel"
                | "class"
                | "select"
                | "abstract"
                | "self"
                | "future"
                | "loop"
                | "async"
                | "await"
                | "private"
                | "func"
        )
    }
    fn is_symbol(ch: char) -> bool {
        matches!(
            ch,
            '+' | '-'
                | '*'
                | '/'
                | '='
                | '('
                | ')'
                | '{'
                | '}'
                | '['
                | ']'
                | ';'
                | ','
                | ':'
                | '.'
                | '<'
                | '>'
                | '&'
                | '|'
                | '^'
                | '!'
                | '#'
                | '_'
                | '$'
                | '?'
                | '`'
                | '@'
                | '~'
        )
    }
        fn to_symbol(ch: char) -> Option<Symbol> {
        match ch {
            '(' => Some(Symbol::LeftParen),
            ')' => Some(Symbol::RightParen),
            '{' => Some(Symbol::LeftBrace),
            '}' => Some(Symbol::RightBrace),
            '[' => Some(Symbol::LeftBracket),
            ']' => Some(Symbol::RightBracket),
            ';' => Some(Symbol::Semicolon),
            ':' => Some(Symbol::Colon),
            ',' => Some(Symbol::Comma),
            '.' => Some(Symbol::Dot),
            '+' => Some(Symbol::Plus),
            '-' => Some(Symbol::Minus),
            '*' => Some(Symbol::Star),
            '/' => Some(Symbol::Slash),
            '%' => Some(Symbol::Percent),
            '=' => Some(Symbol::Equal),
            '<' => Some(Symbol::Less),
            '>' => Some(Symbol::Greater),
            '!' => Some(Symbol::Bang),
            '?' => Some(Symbol::Question),
            '&' => Some(Symbol::Amp),
            '|' => Some(Symbol::Pipe),
            '^' => Some(Symbol::Caret),
            '~' => Some(Symbol::Tilde),
            '@' => Some(Symbol::At),
            '#' => Some(Symbol::Hash),
            '$' => Some(Symbol::Dollar),
            '`' => Some(Symbol::Backtick),
            '"' => Some(Symbol::DoubleQuote),
            '\'' => Some(Symbol::SingleQuote),
            _ => None,
        }
    }
        fn to_keyword(s: &str) -> Option<Keyword> {
        match s {
            "return" => Some(Keyword::Return),
            "break" => Some(Keyword::Break),
            "continue" => Some(Keyword::Continue),
            "if" => Some(Keyword::If),
            "else" => Some(Keyword::Else),
            "elif" => Some(Keyword::Elif),
            "for" => Some(Keyword::For),
            "while" => Some(Keyword::While),
            "loop" => Some(Keyword::Loop),
            "match" => Some(Keyword::Match),
            "fn" => Some(Keyword::Fn),
            "struct" => Some(Keyword::Struct),
            "enum" => Some(Keyword::Enum),
            "const" => Some(Keyword::Const),
            "static" => Some(Keyword::Static),
            "type" => Some(Keyword::Type),
            "as" => Some(Keyword::As),
            "in" => Some(Keyword::In),
            "where" => Some(Keyword::Where),
            "async" => Some(Keyword::Async),
            "await" => Some(Keyword::Await),
            "true" => Some(Keyword::True),
            "false" => Some(Keyword::False),
            "null" => Some(Keyword::Null),
            "new" => Some(Keyword::New),
            "yield" => Some(Keyword::Yield),
            "import" => Some(Keyword::Import),
            "export" => Some(Keyword::Export),
            "from" => Some(Keyword::From),
            _ => None,
        }
    }
}
